#include "lexer.hpp"
#include "ParsingErrorException.hpp"
#include "InputStream.hpp"

Lexer::Lexer(InputStream * stream)
: yyFlexLexer()
, yylval()
, yylloc()
, position_(stream->position())
, preprocessor_(stream)
{
	position_.newChar(); //HACK
}

Lexer::~Lexer()
{

}

int Lexer::LexerInput(char * buf_data, int max_size )
{
	return preprocessor_.read(buf_data, max_size);
}

void Lexer::LexerError( const char* msg )
{
	throw ParsingErrorException(SourceLocation(), msg);
}

void Lexer::prepareForAction()
{
	yylval = ObjectPtr();
	yylloc.setBegin(position_);
	position_.newChars(yyleng - 1);
	yylloc.setEnd(position_);
	position_.newChar();
}

static int digitValue(char c, int base)
{
	int val = 0;
	if(c >= '0' && c <= '9')
	{
		val = c - '0';
	}
	else if(c >= 'a' && c <= 'z')
	{
		val = 10 + (c - 'a');
	}
	else if(c >= 'A' && c <= 'Z')
	{
		val = 10 + (c - 'A');
	}
	else
	{
		val = -1;
	}

	return val < base ? val : -1;
}

String Lexer::unescape(const char *str)
{
	static char const escapeTable[] = "a\ab\bf\fn\nr\rt\t\\\\\'\'\"\"";

	enum { Normal, Slash, Hex } state = Normal;

	String retval;
	int baseLog2 = -1;
	int charCode = 0;
	int digVal = -1;

	while(*str)
	{
		char c = *(str++);

		switch(state)
		{
		case Normal:
			if(c == '\\')
				state = Slash;
			else
				retval.push_back(c);
			break;
		case Slash:
			if(c == '\\')
			{
				retval.push_back('\\');
				state = Normal;
			}
			else if(c == 'x' || c == 'X')
			{
				baseLog2 = 4;
				state = Hex;
			}
			else if(c >= '0' && c <='7')
			{
				--str;
				baseLog2 = 3;
				state = Hex;
			}
			else
			{
				char const * pEsc = escapeTable;
				for(;;)
				{
					if(!*pEsc)
					{
						ParsingErrorException ex(
							SourceLocation(),
							"Unrecognized escape sequence"
						);
					}
					char c1 = *(pEsc++);
					char c2 = *(pEsc++);

					if(c == c1)
					{
						retval.push_back(c2);
						state = Normal;
						break;
					}
				}
			}
			break;
		case Hex:
			digVal = digitValue(c, 1 << baseLog2);
			if(digVal >= 0)
			{
				charCode = (charCode << baseLog2) | digVal;
				if(charCode > 255)
				{
					throw ParsingErrorException(
						SourceLocation(),
						"Value of the character code in escape sequence is out of range"
					);
					
				}
			}
			else
			{
				--str;
				retval.push_back((char)charCode);
				charCode = 0;
			}
			break;
		}
	}
	return retval;
}

int Lexer::parseInt(char const * str, int base)
{
	char * pEnd = 0;
	unsigned long r = strtoul(str, &pEnd, base);
	(void)pEnd;
	//TODO: add error and overflow checks;
	return (int)r;
}

double Lexer::parseReal(char const * str)
{
	return atof(str);
}
